package support

import (
	"gitee.com/gcom/stockdock/comm"
	"github.com/gorilla/context"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"go.uber.org/zap"
	"net/http"
	"reflect"
	"time"
)

const TracerCtxKey = "tracer-ctx"

var decorators []TracerCtxDecor

type TracerCtxDecor = func(TracerCtx)
type TracerCtx *tracerCtx
type tracerCtx struct {
	TraceId uint64
	Req     *http.Request
	Resp    http.ResponseWriter
	Values  map[string]interface{}
	Ts      time.Time
	Logger  *zap.SugaredLogger
	DB      *gorm.DB
}

func newTracerCtx(w http.ResponseWriter, r *http.Request) TracerCtx {
	traceId, err := comm.DefaultIdFlake.NextID()
	if err != nil {
		comm.Log.Errorf("generate traceId error: %w", err)
		panic("generate traceId error")
	}

	ctx := &tracerCtx{
		TraceId: traceId,
		Req:     r,
		Resp:    w,
		Values:  make(map[string]interface{}),
		Ts:      time.Now(),
	}
	for _, d := range decorators {
		d(ctx)
	}
	return ctx
}

func GetTracerCtx(w http.ResponseWriter, r *http.Request) TracerCtx {
	if ctx := context.Get(r, TracerCtxKey); ctx != nil {
		if ctx, ok := ctx.(TracerCtx); ok {
			return ctx
		}
	}

	ctx := newTracerCtx(w, r)
	if r != nil {
		context.Set(r, TracerCtxKey, ctx)
	}
	return ctx
}

func TracerCtxFrom(from string) TracerCtx {
	ctx := GetTracerCtx(nil, nil)
	ctx.Logger = ctx.Logger.With(zap.String("from", from))
	return ctx
}

func RegisterTracerCtxDecor(dec TracerCtxDecor) {
	for i := 0; i < len(decorators); i++ {
		if reflect.ValueOf(decorators[i]).Pointer() == reflect.ValueOf(dec).Pointer() {
			return
		}
	}
	decorators = append(decorators, dec)
}
